﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Web;
using System.Net;
using System.Security;

namespace FusionDotNet
{
    /// <summary>
    /// Executes an authentication request against the Google API.
    /// </summary>
    public class AuthenticationCommand : PostCommand<AuthenticationResult>
    {
        private AccountTypeEnum m_AccountType;
        private string m_EmailAddress;
        private string m_Password;
        private string m_ServiceName;
        private string m_Source;

        /// <summary>
        /// Constructs a new AuthenticationCommand.
        /// </summary>
        /// <param name="accountType"><see cref="AccountTypeEnum"/> enum for the account type.</param>
        /// <param name="emailAddress">Account email address.</param>
        /// <param name="password">Account password.</param>
        /// <param name="serviceName">Name of the service./</param>
        /// <param name="source">Source of the request.</param>
        public AuthenticationCommand(AccountTypeEnum accountType, string emailAddress, string password, string serviceName, string source)
        {
            m_AccountType = accountType;
            m_EmailAddress = emailAddress;
            m_Password = password;
            m_ServiceName = serviceName;
            m_Source = source;
            CreateContent();
        }

        private void CreateContent()
        {
            StringBuilder builder = new StringBuilder();
            builder.AppendFormat("{0}={1}&", "accountType", HttpUtility.UrlEncode(m_AccountType.ToString()));
            builder.AppendFormat("{0}={1}&", "Email", HttpUtility.UrlEncode(m_EmailAddress));
            builder.AppendFormat("{0}={1}&", "Passwd", HttpUtility.UrlEncode(m_Password));
            builder.AppendFormat("{0}={1}&", "service", HttpUtility.UrlEncode(m_ServiceName));
            builder.AppendFormat("{0}={1}&", "source", HttpUtility.UrlEncode(m_Source));
            builder.AppendFormat("{0}=&", "logintoken");
            builder.AppendFormat("{0}=", "logincaptcha");

            Encoding encoding = GetContentEncoding();
            PostContent = encoding.GetBytes(builder.ToString());
        }

        /// <summary>
        /// Returns the Uri for the request.
        /// </summary>
        /// <returns></returns>
        protected override string GetRequestUri()
        {
            return "https://www.google.com/accounts/ClientLogin";
        }

        /// <summary>
        /// Override of Command Execute to provide custom handling for WebException.
        /// </summary>
        /// <returns></returns>
        public override AuthenticationResult Execute()
        {
            try
            {
                return base.Execute();
            }
            catch (WebException ex)
            {
                return CreateExceptionResult(ex);
            }
        }

        /// <summary>
        /// Creates an <see cref="AuthenticationResult"/> based on the results of the command.
        /// </summary>
        /// <param name="reader">StreamReader containing the results of the operation.</param>
        /// <returns>AuthenticationResult</returns>
        internal override AuthenticationResult CreateResult(StreamReader reader)
        {
            string authResult = reader.ReadToEnd();
            char[] delim = new char[] { '=' };
            string[] parts = authResult.Split(delim);
            return new AuthenticationResult(parts[3]);
        }

        /// <summary>
        /// Creates an <see cref="AuthenticationResult"/> based on the exception provided. This override is used to text for a Web Exception with a Status of 'Forbidden'. This indicates that the user name or password provided is incorrect.
        /// </summary>
        /// <param name="exception">Exception to base the result on.</param>
        /// <returns>AuthenticationResult</returns>
        public override AuthenticationResult CreateExceptionResult(Exception exception)
        {
            if (exception is WebException)
            {
                var response = (HttpWebResponse)((WebException)exception).Response;
                if (response != null && response.StatusCode == HttpStatusCode.Forbidden)
                {
                    var result = new AuthenticationResult(null);
                    result.AddException(new SecurityException("Unable to authenticate. Invalid Username or password."));
                    return result;
                }
            }
            throw exception;
        }
    }
}
